/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.inspur.edp.lcm.metadata.core.index;

import com.inspur.edp.lcm.metadata.api.entity.MetadataPackage;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageForIndex;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.core.MetadataCoreManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

public class MetadataPackageIndexService {

    FileServiceImp fileServiceImp = new FileServiceImp();

    protected String keyConnector = "_";

    protected String line = "-";

    protected String featureInName = "-api-";

    public String location;

    protected String depFileSuffix;

    protected Map<String, MetadataPackageForIndex> metadataPackageIndexHashMap;

    public MetadataPackageIndexService() {
        metadataPackageIndexHashMap = new HashMap<>();
    }

    public void refreshMetadataPackageIndex() {
        // 初始化
        if (metadataPackageIndexHashMap.size() == 0) {
            initMetadataPackageIndex();
        } else {
            // 增删改处理
            refreshIndex();
        }
    }

    private void refreshIndex() {
        // 获取所有keys
        List<String> currentKeys = getAllKeysFromLocation();
        List<String> indexKeys = new ArrayList<>(metadataPackageIndexHashMap.keySet());

        // 交集
        List<String> retainKeys = new ArrayList<>(currentKeys);
        retainKeys.retainAll(indexKeys);

        // 新增
        currentKeys.removeAll(retainKeys);

        // 减少
        indexKeys.removeAll(retainKeys);

        delete(indexKeys);

        insert(currentKeys);

        update(retainKeys);
    }

    private void delete(List<String> deleteKeys) {
        for (String key : deleteKeys) {
            if (metadataPackageIndexHashMap.containsKey(key)) {
                metadataPackageIndexHashMap.remove(key);
            }
        }
    }

    private void update(List<String> retainKeys) {
        for (String key : retainKeys) {
            String[] fileNameArray = key.split(keyConnector);
            if (!fileNameArray[1].contains(line) || fileNameArray[1].endsWith(Utils.getVersionSuffix())) {
                Long lastModified = new File(location + File.separator + fileNameArray[1] + File.separator + fileNameArray[0]).lastModified();
                boolean changeFlag = lastModified - metadataPackageIndexHashMap.get(key).getLastModified() != 0;
                if (changeFlag) {
                    metadataPackageIndexHashMap.get(key).setLastModified(lastModified);
                    getMetadataIdsAndReferencesInSinglePackage(key);
                }
            }
        }
    }

    private void insert(List<String> insertKeys) {
        for (String key : insertKeys) {
            String[] fileNameArray = key.split(keyConnector);
            File dir = new File(location + File.separator + fileNameArray[1]);
            getSingleMetadataPackageForIndex(dir);
        }
        for (String key : insertKeys) {
            getMetadataIdsAndReferencesInSinglePackage(key);
        }
    }

    private List<String> getAllKeysFromLocation() {
        List<String> metadataPackageIndexKeys = new ArrayList<>();
        List<File> dirs = fileServiceImp.getDirectorys(location);
        for (File dir : dirs) {
            File[] files = dir.listFiles();
            String key;
            for (File file : files) {
                if (file.getName().endsWith(Utils.getMetadataPackageExtension())) {
                    key = handleKey(file.getName(), dir.getName());
                    metadataPackageIndexKeys.add(key);
                }
            }
        }
        return metadataPackageIndexKeys;
    }

    private void initMetadataPackageIndex() {
        // 初始metadatapackage
        getAllMetadataPackages();

        // 获取metadataIds和depMetadataPackages
        getMetadataIdsAndReferences();
    }

    private void getAllMetadataPackages() {
        // 获取location下所有文件夹
        List<File> dirs = fileServiceImp.getDirectorys(location);
        for (File dir : dirs) {
            getSingleMetadataPackageForIndex(dir);
        }
    }

    protected void getSingleMetadataPackageForIndex(File dir) {
        File[] files = dir.listFiles();
        MetadataPackageForIndex metadataPackageForIndex = new MetadataPackageForIndex();
        String metadataPackageForIndexKey = "";
        for (File file : files) {
            if (file.getName().endsWith(Utils.getMetadataPackageExtension())) {
                metadataPackageForIndexKey = handleKey(file.getName(), dir.getName());
                metadataPackageForIndex.setMetadataPackageName(file.getName());
                metadataPackageForIndex.setLocation(file.getAbsolutePath());
                metadataPackageForIndex.setLastModified(file.lastModified());
                metadataPackageForIndex.setSourceName(dir.getName());
                if (dir.getName().contains(featureInName)) {
                    String sourceVersion = dir.getName().substring(dir.getName().lastIndexOf(featureInName) + featureInName.length());
                    metadataPackageForIndex.setSourceVersion(sourceVersion);
                }
            }
            if (depFileSuffix != null && file.getName().endsWith(depFileSuffix)) {
                metadataPackageForIndex.setDepFileLocation(file.getAbsolutePath());
            }
        }
        if (metadataPackageForIndexKey != "") {
            metadataPackageIndexHashMap.put(metadataPackageForIndexKey, metadataPackageForIndex);
        }
    }

    protected String handleKey(String metadataPackageName, String name) {
        String key = metadataPackageName + keyConnector + name;
        return key;
    }

    private void getMetadataIdsAndReferences() {
        metadataPackageIndexHashMap.keySet().forEach(key -> {
            getMetadataIdsAndReferencesInSinglePackage(key);
        });
    }

    private void getMetadataIdsAndReferencesInSinglePackage(String metadataPackageIndexKey) {
        // 获取元数据包信息
        MetadataCoreManager metadataManager = new MetadataCoreManager();
        if (!metadataPackageIndexHashMap.containsKey(metadataPackageIndexKey)) {
            return;
        }
        String packageLocation = fileServiceImp.getDirectoryName(metadataPackageIndexHashMap.get(metadataPackageIndexKey).getLocation());
        MetadataPackage metadataPackageInfo = metadataManager.getMetadataPackageInfo(metadataPackageIndexHashMap.get(metadataPackageIndexKey).getMetadataPackageName(), packageLocation);

        // 获取元数据包中的metadataIds
        getMetadataIdsInSinglePackage(metadataPackageInfo, metadataPackageIndexKey);

        // 获取元数据包中的依赖
        getReferencesInSinglePackage(metadataPackageInfo, metadataPackageIndexKey);
    }

    private void getMetadataIdsInSinglePackage(MetadataPackage metadataPackageInfo, String metadataPackageForIndexKey) {
        List<String> metadataIds = new ArrayList<>();
        metadataPackageInfo.getMetadataList().forEach(metadata -> {
            metadataIds.add(metadata.getHeader().getId());
        });
        metadataPackageIndexHashMap.get(metadataPackageForIndexKey).setMetadataIds(metadataIds);
    }

    private void getReferencesInSinglePackage(MetadataPackage metadataPackageInfo, String metadataPackageForIndexKey) {
        Map<String, MetadataPackageForIndex> depMetadataPackages = new HashMap<>();
        // 添加maven依赖
        String depFileLocation = metadataPackageIndexHashMap.get(metadataPackageForIndexKey).getDepFileLocation();
        if (depFileLocation != null) {
            Model model = getModel(depFileLocation);
            model.getDependencies().forEach(dependency -> {
                String gav = dependency.getGroupId() + line + dependency.getArtifactId() + line + dependency.getVersion();
                metadataPackageIndexHashMap.keySet().forEach(key -> {
                    if (key.endsWith(gav)) {
                        depMetadataPackages.put(key, metadataPackageIndexHashMap.get(key));
                        return;
                    }
                });
            });
        }
        // 添加元数据包依赖
        if (metadataPackageInfo != null && metadataPackageInfo.getReference() != null) {
            metadataPackageInfo.getReference().forEach(depMetadataPackage -> {
                boolean existFlag = false;
                String depMetadataPackageName = depMetadataPackage.getDepententPackage().getName() + Utils.getMetadataPackageExtension();
                for (String key : depMetadataPackages.keySet()) {
                    if (key.startsWith(depMetadataPackageName)) {
                        existFlag = true;
                        break;
                    }
                }
                if (!existFlag) {
                    String key = depMetadataPackageName + keyConnector + depMetadataPackageName;
                    depMetadataPackages.put(key, MetadataPackageIndexServiceForPackages.getInstance(null).getMetadataPackageIndexForPackagesHashMap().get(key));
                }
            });
        }

        metadataPackageIndexHashMap.get(metadataPackageForIndexKey).setDepMetadataPackages(depMetadataPackages);
    }

    private Model getModel(String depFileLocation) {
        try (FileInputStream fis = new FileInputStream(depFileLocation)) {
            MavenXpp3Reader reader = new MavenXpp3Reader();
            final Model model = reader.read(fis);
            return model;
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
        }
        return null;
    }
}
